const EventEmitter = require('events')
const http = require('http')
const context = require('./context')
const request = require('./request')
const response = require('./response')
const Stream = require('stream')

class Koa extends EventEmitter {
    constructor() {
        super()
        this.context = Object.create(context) //  this.context.__proto__ === context Object.create生成一个obj的实例 类似new Fn 
        this.request = Object.create(request)
        this.response = Object.create(response)
        this.middleware = []
    }

    use(fn) {  // 收集 中间件
        this.middleware.push(fn)
    }

    createContext = (req, res) => {  // 创建上下文
        this.context = Object.create(context)
        this.request = Object.create(request)
        this.response = Object.create(response)
        this.context.request = this.request
        this.context.response = this.response
        this.context.req = this.context.request.req = req
        this.context.res = this.context.response.res = res
        return this.context
    }
    compose = (ctx) => {  // 执行中间件
        let lastIndex = -1 //  是否重复调用next方法
        const dispatch = (i) => {
            if (i <= lastIndex) return Promise.reject('next 被重复调用')
            lastIndex = i
            if (this.middleware.length === i) return Promise.resolve()
            return Promise.resolve(this.middleware[i](ctx, () => dispatch(++i)))
        }
        return dispatch(0)
    }

    handleRequest(req, res) {
        let ctx = this.createContext(req, res)
        ctx.res.statusCode = 404
        this.compose(ctx).then(() => {  // 中间件可能存在异步操作
            if (ctx.body) {
                if (ctx.body instanceof Stream) {
                    res.setHeader('Content-type', 'application/json;charset=utf-8')
                    ctx.body.pipe(res)
                } else if (typeof ctx.body === 'object') {
                    res.setHeader('Content-type', 'application/json;charset=utf-8')
                    res.end(JSON.stringify(ctx.body))
                } else {
                    res.end(ctx.body)
                }
            } else {
                res.end('not found')
            }
        }).catch(e => {
            this.emit('error', e)
        })
    }

    listen(...args) {
        let server = http.createServer(this.handleRequest.bind(this))
        server.listen(...args)
    }
}

module.exports = Koa